
#define MAX(x,y) ((x) > (y) ? (x) : (y))

#ifndef SS_MAX_LISTEN_CONNECTIONS
#   define SS_MAX_LISTEN_CONNECTIONS (128)
#endif


Socket_Server_T::Socket_Server_T(unsigned short port_number,
                                 unsigned short secure_port_number,
                                 char const * log_file,
                                 size_t max_log_file_size)
                :m_accept_fd(-1),
                 m_broadcast_map(),
                 m_clients_by_uid(),
                 m_exit_requested(false),
                 m_json_logger(NULL),
                 m_last_soh_time(),
                 m_pending_clients(),
                 m_port_number(port_number),
                 m_reset_cache(false),
                 m_secure_accept_fd(-1),
                 m_secure_port_number(secure_port_number),
                 m_soh_enabled(true),
                 m_spies_from(),
                 m_spies_to(),
                 m_spy_domain_map(),
                 m_spy_from_client(NULL),
                 m_spy_from_uid(0),
                 m_spy_message_code_map(),
                 m_spy_to_client(NULL),
                 m_spy_to_uid(0)
{

}

Socket_Server_T::~Socket_Server_T()
{
   delete m_json_logger;
   close_connection(m_accept_fd);
   destroy_and_erase_all_clients();
   if (m_secure_accept_fd >= 0)
   {
      close_connection(m_secure_accept_fd);
   }
}

void Socket_Server_T::update_time_of_soh_check(void)
{
	int result = clock_gettime(CLOCK_REALTIME, &m_last_soh_time);
}


int Socket_Server_T::setup_read_set(fd_set* rd_set)
{
   int nfds = 0;
   SS_Client_T* client;

   FD_ZERO(rd_set);
   // Add FD for accepting new connections to rd_set
   FD_SET(m_accept_fd, rd_set);
   nfds = MAX(nfds, m_accept_fd);
   if (m_secure_accept_fd >= 0)
   {
      // Add FD for accepting new secure connections to rd_set
      FD_SET(m_secure_accept_fd, rd_set);
      nfds = MAX(nfds, m_secure_accept_fd);
   }
   // Add FDs for all active clients to rd_set
   std::map<uint32_t, SS_Client_T *>::iterator uid_it;
   for (uid_it = m_clients_by_uid.begin(); uid_it != m_clients_by_uid.end(); ++uid_it)
   {
      client = (*uid_it).second;
      int client_fd = client->Get_Socket_FD();
      FD_SET(client_fd, rd_set);
      nfds = MAX(nfds, client_fd);
   }
   // Add FDs for all pending clients to set
   std::map<int, SS_Client_T *>::iterator fd_it;
   for (fd_it = m_pending_clients.begin(); fd_it != m_pending_clients.end(); ++fd_it)
   {
      client = (*fd_it).second;
      int client_fd = client->Get_Socket_FD();
      FD_SET(client_fd, rd_set);
      nfds = MAX(nfds, client_fd);
   }
   return nfds;
}

int Socket_Server_T::setup_write_set(fd_set* wr_set)
{
   int nfds = 0;

   FD_ZERO(wr_set);
   // Add FDs for all active clients with pending output to wr_set
   std::map<uint32_t, SS_Client_T *>::iterator client_it;
   for (client_it = m_clients_by_uid.begin(); client_it != m_clients_by_uid.end(); ++client_it)
   {
      SS_Client_T* client = (*client_it).second;
      if (client->Output_Is_Pending())
      {
         int client_fd = client->Get_Socket_FD();
         FD_SET(client_fd, wr_set);
         nfds = MAX(nfds, client_fd);
      }
   }
   return nfds;
}

void Socket_Server_T::close_connection(int & accept_fd)
{
   if (accept_fd != -1)
   {
      if (shutdown(accept_fd, SHUT_RDWR) != 0)
      {
         printf("shutdown() FAILED");
      }
      if (close(accept_fd) != 0)
      {
         printf("close() FAILED");
      }
      accept_fd = -1;
   }
}

void Socket_Server_T::create_connection(unsigned short port_number, int & accept_fd)
{
	close_connection(accept_fd);
	
	while(accept_fd < 0)
	{
		accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if(accept_fd < 0)
		{
			printf("");
		}
		else
		{
			struct sockaddr_in socket_adr;
         	int result = 0;
         	int sock_opt = 1;
			int listen_result;

			result = setsockopt(accept_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, sizeof sock_opt);
			result = setsockopt(accept_fd, IPPROTO_TCP, TCP_NODELAY, &sock_opt, sizeof sock_opt);

			memset(&socket_adr, 0, sizeof(socket_adr));

         	socket_adr.sin_family = AF_INET;
         	socket_adr.sin_port = htons(port_number);
         	socket_adr.sin_addr.s_addr = INADDR_ANY;

			result = bind(accept_fd, (struct sockaddr*) (&socket_adr), sizeof(socket_adr));

			/* Listen for a new connection from a client */
			listen_result = listen(accept_fd, SS_MAX_LISTEN_CONNECTIONS);
		}
		
	}
}

void Socket_Server_T::accept_new_client(int accept_fd)
{
	struct sockaddr cli_addr;
   	socklen_t cli_addr_sz = sizeof(cli_addr);

	/* Accept the connection from the client */
   	int client_fd = accept(accept_fd, &cli_addr, &cli_addr_sz);
	if(client_fd < 0)
	{
		printf("Could not accept...");
	}
	else
	{
		if (accept_fd == m_secure_accept_fd) // This is an SSL connection
    	{
        	m_pending_clients[client_fd] = new SSL_Client_T(*this, client_fd);
      	}
      	else // This is an unsecured connection
      	{
        	m_pending_clients[client_fd] = new SS_Client_T(*this, client_fd);
      	}
	}
}

void Socket_Server_T::read_from_clients(fd_set* rd_set)
{
   /*
    * Read from "active" clients
    */
   std::map<uint32_t, SS_Client_T *>::iterator next_client_it;
   std::map<uint32_t, SS_Client_T *>::iterator client_it;
   for (client_it = m_clients_by_uid.begin(); client_it != m_clients_by_uid.end(); client_it = next_client_it)
   {
      next_client_it = client_it;
      ++next_client_it; // Have to get next iterator before delete invalidates this one.
      SS_Client_T* client = (*client_it).second;
      int client_fd = client->Get_Socket_FD();
      if (client->Is_Connected() && FD_ISSET(client_fd, rd_set))
      {
         client->Receive();
      }
      // If the client was shut down, remove it from the active list
      if (!client->Is_Connected())
      {
         // NOTE: The next iterator is obtained prior to the call that deletes
         //       what the current iterator points to so the result is valid.
         delete_client(client_it);
      }
   }
   /*
    * Read from "pending" clients
    */
   std::map<int, SS_Client_T *>::iterator next_pending_it;
   std::map<int, SS_Client_T *>::iterator pending_it;
   for (pending_it = m_pending_clients.begin(); pending_it != m_pending_clients.end(); pending_it = next_pending_it)
   {
      next_pending_it = pending_it;
      ++next_pending_it; // Have to get next iterator before move or delete invalidates this one.
      // Use local copy of client pointer since iterator may become invalid.
      SS_Client_T* client = (*pending_it).second;

      int client_fd = client->Get_Socket_FD();
      /*
       * Reading from the client may invalidate the iterator by moving client
       * to m_clients_by_uid. If the client is deleted because it is no longer
       * connected, that will also invalidate the iterator. SO, increment the
       * iterator now while it's guaranteed to be valid.
       */
      if (client->Is_Connected() && FD_ISSET(client_fd, rd_set))
      {
         client->Receive(); // MAY move client to m_clients_by_uid
      }
      // If the client was shut down, delete it and remove it from the pending list
      if (!client->Is_Connected())
      {
         delete client;
         m_pending_clients.erase(client_fd);
      }
   }
}

void Socket_Server_T::Run()
{
	update_time_of_soh_check();

	while(!m_exit_requested)
	{
		create_connection(m_port_number, m_accept_fd);

		while(!m_exit_requested)
		{
			fd_set rd_set;
         	fd_set wr_set;
         	int select_result;

			int nfds = setup_read_set(&rd_set);
			nfds = MAX(nfds, setup_write_set(&wr_set));

			struct timespec select_timeout;

         	select_timeout.tv_sec  = (SS_PSELECT_TIMEOUT_MS/1000);
         	select_timeout.tv_nsec = (SS_PSELECT_TIMEOUT_MS % 1000) * 1000000;
         	select_result = pselect(nfds + 1, &rd_set, &wr_set, NULL, &select_timeout, NULL);

			if ((m_accept_fd >= 0) && FD_ISSET(m_accept_fd, &rd_set))
			{
				accept_new_client(m_accept_fd);
			}
			
			// Next, check for any of the clients with data to read
         	read_from_clients(&rd_set);
         	// ... and any of the clients with data to write
         	write_to_clients(&wr_set);
		}

		destroy_and_erase_all_clients();
	}
}








